<?php
/**
 * FastApp.
 * 10/6/24
 * @Author deepsea159
 * @Contact telegram:deepsea159
 */

namespace App\Game\Api\Service\Platform;

use App\Fastapp\Api\Service\Account\WalletService;
use App\Fastapp\Exception\TipException;
use App\Fastapp\Model\AccountWallet;
use App\Fastapp\Model\AccountWalletLog;
use App\Game\Model\Game;
use App\Game\Model\GameAccountRtp;
use App\Game\Model\GameBet;
use App\Game\Model\GamePlatform;
use App\Game\Model\GameSymbol;
use Carbon\Carbon;
use Mine\Helper\Str;

class ApibetService implements GameInterface
{
    const CODE = 'apibet';
    const UUID_PREFIX = 'a';
    const UUID_LENGTH = 11;

    public static array $config = [];

    //文档 https://doc.api-bet.com/docs/api/api-1e42nden6io3d
    const PR = [
        'ag' => ['CNY', 'IDR', 'INR', 'USD', 'VND'],//亚游
        'allbet' => ['CNY', 'USD'],//欧博
        'ap' => ['CNY'],//平博
        'bbin' => ['CNY', 'HKD', 'USD', 'VND',],//宝盈
        'bg' => ['CNY'],//大游
        'boya' => ['CNY'],//博雅
        'cmd' => ['CNY', 'IDR', 'VND'],//CMD
        'cq9' => ['BRL', 'CNY', 'HKD', 'IDR', 'INR', 'MMK', 'PHP', 'THB', 'USD', 'VND',],//CQ9
        'crown' => ['CNY', 'HKD'],//皇冠
        'db1' => ['CNY', 'VND'],//多宝视讯
        'db2' => ['CNY'],//多宝老虎机
        'db3' => ['CNY', 'IDR'],//多宝彩票
        'db5' => ['CNY'],//多宝电竞
        'db6' => ['CNY'],//多宝捕鱼
        'db7' => ['CNY'],//多宝棋牌
        'dg' => ['CNY', 'HKD', 'IDR', 'MMK', 'THB', 'VND'],//DG
        'esb' => ['CNY'],//电竞牛
//        'evo' => ['BRL', 'CNY', 'HKD', 'IDR', 'PHP', 'THB', 'USD', 'VND',],//EVO 不支持
//        'fb' => ['BRL', 'CNY', 'INR'],//FB 不支持
        'fc' => ['CNY', 'IDR', 'PHP', 'THB', 'VND',],//FC
        'fg' => ['CNY', 'NGN', 'THB'],//FG
        'ftg' => ['THB'],//FTG
        'gb' => ['VND'],//GB
        'gw' => ['VND'],//GW
        'hb' => ['VND'],//HB
        'ig' => ['CNY'],//IG
        'im' => ['CNY', 'VND'],//IM
        'jdb' => ['BRL', 'CNY', 'HKD', 'INR', 'PHP', 'THB', 'USD', 'VND',],//夺宝
        'jili' => ['IDR', 'MMK', 'THB', 'VND',],//JILI
        'joker' => ['CNY', 'HKD', 'IDR', 'MMK',],//Joker
        'ka' => ['THB'],//KA
        'km' => ['PHP'],//KingMaker
        'ky' => ['CNY'],//开元
        'leg' => ['CNY'],//乐游
        'lgd' => ['CNY'],//LGD
        'mg' => ['CNY', 'INR', 'PHP', 'USD', 'VND',],//MG
        'mt' => ['CNY'],//美天
        'mw' => ['CNY'],//大满贯
        'newbb' => ['CNY', 'USD'],//NewBB
        'nw' => ['CNY'],//新世界
        'og' => ['CNY'],//东方
        'panda' => ['CNY', 'VND'],//熊猫体育
        'pg' => ['BRL', 'CNY', 'IDR', 'INR', 'MMK', 'PHP', 'THB', 'VND',],//PG
        'png' => ['CNY'],//PNG
        'pp' => ['BRL', 'CNY', 'HKD', 'IDR', 'INR', 'MMK',],//王者
        'ps' => ['THB'],//PS
        'pt' => ['CNY'],//PT
        'r88' => ['THB'],//R88
        'rt' => ['VND'],//RT
        'sa' => ['MMK', 'THB', 'VND',],//沙龙
        'saba' => ['CNY', 'VND'],//沙巴
        'sbo' => ['IDR', 'VND'],//SBO
        'sexy' => ['CNY', 'HKD', 'IDR', 'PHP', 'VND',],//Sexy
        'sg' => ['CNY', 'IDR'],//SG
        'sgwin' => ['CNY'],//双赢
        'ss' => ['CNY'],//三昇
        't1' => ['BRL', 'INR'],//T1game
        'tcg' => ['IDR', 'VND'],//天成
        'tf' => ['CNY', 'HKD', 'VND'],//雷火电竞
        'ttg' => ['VND'],//TTG
        'ug' => ['THB'],//UG
        'v8' => ['CNY', 'MMK', 'VND'],//V8 Poker
        'vg' => ['CNY'],//财神棋牌
        'vr' => ['CNY', 'IDR'],//VR
        'we' => ['CNY', 'USD'],//WE
        'wl' => ['CNY'],//瓦力
        'wm' => ['CNY', 'HKD', 'USD', 'VND',],//完美
        'ww' => ['CNY', 'VND'],//双赢
        'xg' => ['THB'],//XG
        'xgd' => ['CNY'],//高登
        'xj' => ['CNY'],//小金
        'yoo' => ['CNY'],//云游
    ];

    const DEMO = [
        'ww' => 'https://sgdemo.qpglab.com',
        'bbin' => 'https://sgdemo.qpglab.com',
        'wm' => '', 'we' => '', 'sg' => '', 'saba' => '',
        'pt' => '', 'pp' => '', 'png' => '', 'mg' => '',
        'lgd' => '', 'ky' => '', 'jdb' => '', 'fg' => '',
        'dg' => '', 'cmd' => '', 'bg' => '', 'ag' => '',
    ];

    public static function syncGameList(): bool
    {
        $rtp = self::getConfig()['rtp'];
        foreach (explode(',', self::getConfig()['provider']) as $provider) {
            $in = [];
            $exist = Game::query()->where(['platform' => self::CODE, 'provider' => $provider])->pluck('code')->toArray();
            $t = date('Y-m-d H:i:s');
            $res = self::post('/api/server/gameCode', ['platType' => $provider]);
            if (empty($res['data'])) continue;
            foreach ($res['data'] as $item) {
                if (in_array($item['gameCode'], $exist)) continue;
                $name = [];
                foreach ($item['gameName'] as $lang => $v) {
                    $name[] = [
                        'lang' => str_replace('-', '_', $lang),
                        'text' => $v
                    ];
                }
                $in[] = [
                    'name' => json_encode($name, JSON_UNESCAPED_UNICODE),
                    'platform' => self::CODE,
                    'provider' => $provider,
                    'code' => $item['gameCode'],
                    'rtp' => $rtp,
                    'type' => $item['gameType'],
//                    'cover' => $item['banner'],
                    'status' => 1,
                    'ingress' => $item['ingress'],
                    'created_at' => $t,
                    'updated_at' => $t,
                ];
            }
            if ($in) Game::query()->insert($in);
        }
        self::syncSymbol();
        return true;
    }

    public static function syncSymbol(): void
    {
        foreach (explode(',', self::getConfig()['provider']) as $provider) {
            $map = ['platform' => self::CODE, 'provider' => $provider];
            $game_ids = Game::query()->where($map)->pluck('id')->toArray();
            GameSymbol::query()->whereIn('game_id', $game_ids)->delete();
            $in = [];
            foreach ($game_ids as $gid) {
                foreach (self::PR[$provider] as $symbol) {
                    $in[] = [
                        'game_id' => $gid,
                        'symbol' => $symbol
                    ];
                }
            }
            GameSymbol::query()->insert($in);
        }
    }

    public static function gamePlayUrl(array $data): string
    {
        $k = 'gp:' . $data['user_id'];
        $symbol = get_user_cache('symbol', $data['user_id']);
        $uri = '/api/server/gameUrl';
        if ($data['demo'] && isset(self::DEMO[$data['provider']])) {
            if (self::DEMO[$data['provider']]) return self::DEMO[$data['provider']];
            $uri = '/api/server/demoUrl';
        } else {
            if (!redis()->setnx($k, 1)) throw new TipException(fastapp_t('fastapp.operate_frequently'), 7001);
            $balance = AccountWallet::query()->where(['user_id' => $data['user_id'], 'symbol' => $symbol])->value('balance');
            if (bccomp($balance, '1', 2) == -1) {
                redis()->del($k);
                throw new TipException(fastapp_t('account.balance_not_enough'));
            }
            $res = self::post('/api/server/transfer', [
                'playerId' => self::getUUid($data['user_id']),
                'platType' => $data['provider'],
                'currency' => $symbol,
                'type' => 1,
                'amount' => bcadd($balance, '0', 2)
            ]);
            if ($res['code'] != 10000) {
                sys_notify($data['provider'] . '余额不足|' . '当前余额:' . var_export(self::getQuota($symbol), true));
                redis()->del($k);
                throw new TipException(fastapp_t('game.game_maintaining'));
            }
            set_user_cache(['game_balance' => $balance], $data['user_id']);
        }
        $res = self::post($uri, [
            'playerId' => self::getUUid($data['user_id']),
            'gameCode' => $data['code'],
            'gameType' => $data['type'],
            'platType' => $data['provider'],
            'currency' => $symbol,
            'ingress' => $data['ingress'],
        ]);
        if (empty($res['data']['url'])) {
            redis()->del($k);
            throw new TipException('game.init_fail');
        }
        return $res['data']['url'];
    }

    public static function syncBalance(string $symbol, int $uid): ?AccountWallet
    {
        $res = get_user_cache(['game_balance', 'gameid', 'gamepr'], $uid);
        if (empty($res['gamepr']) || empty($res['game_balance']) || bccomp($res['game_balance'], '0', 2) != 1) return null;
        $k = 'gs:' . $uid;
        if (!redis()->setnx($k, 1)) return null;
        try {
            $balance = self::post('/api/server/balance', [
                'playerId' => self::getUUid($uid),
                'platType' => $res['gamepr'],
                'currency' => $symbol
            ])['data']['balance'];
            $s = bccomp($balance, '1', 2) == -1 ? 0 : 1;
            if ($s == 1) self::post('/api/server/transfer', [
                'playerId' => self::getUUid($uid),
                'platType' => $res['gamepr'],
                'currency' => $symbol,
                'type' => 2,
                'amount' => bcadd($balance, '0', 2)
            ]);
            $s2 = !$s && bccomp($balance, '0', 2) == 1;
            if ($s2) self::transferAll($uid);
            $ord = [
                'user_id' => $uid,
                'symbol' => $symbol,
                'type' => AccountWalletLog::USE_BALANCE,
                'object_type' => AccountWalletLog::OBJ_TYPE_GAME,
                'object_id' => $res['gameid'],
            ];
            $result = bccomp('' . $balance, $res['game_balance'], 2);
            set_user_cache(['game_balance' => ''], $uid);
            $wallet = null;
            if ($result == 1) {
                $ord['amount'] = bcsub('' . $balance, $res['game_balance'], 2);
                $ord['type'] = AccountWalletLog::RECHARGE;
                $setting = api_cache()->getAppSetting(get_user_cache('type', $uid));
                $up['pump'] = bcmul($ord['amount'], bcdiv($setting['pump_bet'], '100', 4));
                $ord['amount'] = bcsub($ord['amount'], $up['pump'], 4);
                $wallet = WalletService::inc($ord);
            }
            if ($result == -1) {
                $ord['amount'] = bcsub($res['game_balance'], '' . $balance, 2);
                $wallet = WalletService::dec($ord);
            }
            redis()->del($k);
            redis()->del('gp:' . $uid);
            return $wallet;
        } catch (\Throwable $throwable) {
            sys_notify('syncBalance:' . $throwable->getMessage() . '|' . $throwable->getLine() . '|' . $throwable->getFile());
            redis()->del($k);
        }
        return null;
    }

    public static function createUser(int|string $uid, string $symbol, string|int $provider = ''): void
    {
        $map = [
            'code' => self::CODE,
            'user_id' => $uid,
            'symbol' => $symbol,
            'provider' => $provider,
        ];
        $md = GameAccountRtp::query()->where($map)->first();
        if (!$md) {
            $data = ['playerId' => self::getUUid($uid), 'platType' => $provider, 'currency' => $symbol];
            $res = self::post('/api/server/create', $data);
            if ($res['code'] == 10000 || $res['code'] == 10002) {
                $rtp = GamePlatform::query()->where(['code' => self::CODE])->value('rtp');
                $map['rtp'] = $rtp;
                GameAccountRtp::query()->create($map);
                return;
            }
            sys_notify('init_user_fail:' . json_encode($res) . '|' . json_encode($data));
            throw new TipException('game.init_user_fail');
        }
    }

    public static function getUUid(int $user_id): string
    {
        return str_pad('' . $user_id, self::UUID_LENGTH, self::UUID_PREFIX, STR_PAD_LEFT);
    }

    public static function getUid(string $uuid): string
    {
        return trim($uuid, self::UUID_PREFIX);
    }

    public static function killUser(int|string $user_id): bool
    {
        return true;
    }

    public static function setUserRtp(array $data): bool
    {
        return true;
    }

    public static function getBalance(array $data): array
    {
        return [];
    }

    public static function transaction(array $data = []): array
    {
        if (!self::getConfig()['status']) return [];
        $param = [
            'currency' => 'BRL',
            'pageSize' => '2000',
            'pageNo' => '1',
        ];
        $res = self::post('/api/server/recordAll', $param);
        $in = [];
        $last_bet_id = GameBet::query()->where(['platform' => self::CODE])->orderByDesc('id')->value('bet_id');
        foreach ($res['data']['list'] ?? [] as $item) {
            if ($item['gameOrderId'] == $last_bet_id) $in = [];
            $result_amount = abs($item['settledAmount']);
            if ($item['settledAmount'] > 0) $result = 2; else $result = 3;
            $in[] = [
                'user_id' => self::getUid($item['playerId']),
                'game_id' => 0,
                'game_name' => $item['gameName'],
                'platform' => self::CODE,
                'bet_id' => $item['gameOrderId'],
                'transaction_id' => $item['gameOrderId'],
                'amount' => $item['validAmount'],
                'payout' => 0,
                'result_amount' => abs($result_amount),
                'rtp' => 0,
                'symbol' => 'BRL',
                'status' => 1,
                'result' => $result,
                'created_at' => Carbon::createFromFormat('Y-m-d H:i:s', $item['betTime'], 'UTC')->setTimezone('America/Sao_Paulo')->toDateTimeString(),
                'updated_at' => Carbon::createFromFormat('Y-m-d H:i:s', $item['lastUpdateTime'], 'UTC')->setTimezone('America/Sao_Paulo')->toDateTimeString(),
            ];
        }
        if ($in) GameBet::query()->insert($in);
        return [];
    }

    public static function transferAll(int $uid): array
    {
        $res = self::post('/api/server/transferAll', [
            'playerId' => self::getUUid($uid),
            'currency' => get_user_cache('symbol', $uid)
        ]);
        return $res['data'];
    }

    public static function getQuota(string $symbol): array
    {
        $res = self::post('/api/server/quota', ['currency' => $symbol]);
        $data = [];
        foreach ($res['data'] as $k => $v) {
            $data[str_replace('-BRL', '', $k)] = $v;
        }
        return $data;
    }

    public static function getConfig(bool $refresh = false): array
    {
        if (!self::$config || $refresh) {
            $md = GamePlatform::query()->where(['code' => self::CODE])->first(['config', 'rtp', 'status']);
            self::$config = array_column($md->config, 'text', 'field');
            self::$config['url'] = self::$config[self::$config['use_url']];
            self::$config['rtp'] = $md->rtp;
            self::$config['status'] = $md->status;
        }
        return self::$config;
    }

    public static function post(string $uri, array $data = []): array
    {
        $config = self::getConfig();
        $random_str = Str::random(16, 3, '0123456789');
        $sign = md5($random_str . $config['sn'] . $config['secret_key']);
        $curl = curl_init();
        $header = [
            'sign: ' . $sign,
            'random: ' . $random_str,
            'sn: ' . $config['sn'],
            'Content-Type: application/json'
        ];
        curl_setopt_array($curl, array(
            CURLOPT_URL => $config['url'] . $uri,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => '',
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 0,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => 'POST',
            CURLOPT_POSTFIELDS => json_encode($data),
            CURLOPT_HTTPHEADER => $header,
        ));
        $response = curl_exec($curl);
//        $error = curl_error($curl);
//        var_dump($error);
        curl_close($curl);
        if (empty($response)) throw new \Exception('请勿频繁请求', 500);
        return json_decode($response, true);
    }
}